home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 11 - 1995 / 11.04 Apr 95 / Performance / Fractal 6 / FractalEngine.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-13  |  14.0 KB  |  588 lines  |  [TEXT/MPS ]

  1. /*    
  2.     File:        FractalEngine.c
  3.  
  4.     Used to build:    “Fractal 6”
  5.     
  6.     Written by:        Jim Cathey            July 1985
  7.                     Eric Traut            November 1994
  8.  
  9.     Description:
  10.         The following code implements a “Fractal Contour” generating
  11.         program. See comments in the file “FractalMain.c” for more
  12.         information.
  13.         
  14.         The code in this file implements the fractal-generating
  15.         and plotting portion of the program.
  16. */
  17.  
  18.  
  19. #include <Quickdraw.h>
  20. #include <ToolUtils.h>
  21.  
  22. #include <stdlib.h>
  23.  
  24. #include "Fractal.h"
  25.  
  26. /* Functions defined within this file */
  27. static double RandomDouble(void);
  28. static void CalcXs(long len, long incr, long sk);
  29. static void CalcYs(long len, long incr, long sk);
  30. static void CalcDiagonals(long len, long incr, long sk);
  31. static long GetZValue(long x, long y);
  32. static void SetZValue(long d, long x, long y);
  33. static void SeaLevel(long *newx, long *newy, long *newz);
  34. static void DoPlot(long xindex, long yindex);
  35. static void PlotTo(long x, long y, long z);
  36. static void DrawLine(long x, long y);
  37. static void Cordic(long *x, long *y, long scale, long count);
  38. static void ScaledMoveTo(long x, long y);
  39. static void ScaledLineTo(long x, long y);
  40. static void ScaledPoint(long x, long y);
  41. static void SetLineColor(unsigned short colorCode);
  42. static void LineToOffscreen(long x, long y);
  43. static void LineXOffscreen(long startX, long startY, long endX, long endY);
  44. static void LineYOffscreen(long startX, long startY, long endX, long endY);
  45. static void PointOffscreen(long x, long y);
  46.  
  47. /* Global variables */
  48. long             gMaxX, gMaxY;
  49. long            gLevel;                    /* Current level */
  50. long            gCurrentX, gCurrentY;
  51. unsigned char    gPixelColor;            /* Pixel value for offscreen line drawing */
  52. Boolean         gAtLineStart;            /* True at the first of the line */
  53. Boolean         gOnLand;                /* True when plotting land, else false */
  54. char*            gPixelBase;                /* Base of pixel store */
  55. long            gRowBytesIncrement;        /* Amount to increment each offscreen row */
  56. long            gWindowScaleX;            /* Amount to scale points in X direction */
  57. long            gWindowScaleY;            /* Amount to scale points in Y direction */
  58.  
  59. /* Macros */
  60. #define Rotate(x, y)  Cordic((x), (y), 3, 4)
  61. #define TiltDown(x, z) Cordic((x), (z), 3, (gContourType == kStyleMountains) ? 5 : -5)
  62. #define AbsoluteValue(v) ((v) > 0 ? (v) : -(v))
  63.  
  64. /*
  65.     RandomDouble
  66.     
  67.     This function returns a random number between -1 and 1.
  68. */
  69. double RandomDouble(void)
  70. {
  71.     return (double)rand() / RAND_MAX;
  72. }
  73.  
  74.  
  75. /*
  76.     CalcSurface
  77.     
  78.     This is the main surface-generating routine. It generates a random
  79.     fractal surface by filling in the gPointArray array.
  80. */
  81. void CalcSurface(unsigned short level)
  82. {
  83.     long             i, j, length, incrby, sk;
  84.     float             power;
  85.     long            startTicks;
  86.         
  87.     gLevel = level;
  88.  
  89.     gTotalFractals++;
  90.     gFractalChanged = true;
  91.     startTicks = TickCount();
  92.     
  93.     gMaxX = 1 << level;
  94.     gMaxY = gMaxX / 2;
  95.     for (i = 0; i <= gMaxX; i++)           /* Clear the Array.  Use i & incrby as temps */
  96.     for (incrby = 0; incrby <= gMaxY; incrby++)
  97.         (*gPointArray)[i][incrby] = 0;
  98.  
  99.     for (i = 1; i <= level; i++) {
  100.         for (power = 1.0, j = 0; j < i; j++)
  101.             power *= 1.8;
  102.         length = 10000 / power;            /* = 10000/(1.8^i) */
  103.         incrby = gMaxX / (1 << i);         /* # of line segments in a side of the triangle */
  104.         sk = incrby * 2;
  105.         CalcXs(length, incrby, sk);
  106.         CalcYs(length, incrby, sk);
  107.         CalcDiagonals(length, incrby, sk);
  108.     }
  109.     
  110.     gTotalTickCount += TickCount() - startTicks;
  111. }
  112.  
  113.  
  114. /*
  115.     CalcXs
  116.     
  117.     This function calculates the fractal values in the X direction.
  118.     It is called for each level and subdivides the existing edges
  119.     of the fractal to get the next level.
  120. */
  121. void CalcXs(long len, long incr, long sk)
  122. {
  123.     long             y, x;
  124.     long             d1, d2;
  125.  
  126.     for (y=0; y < gMaxX; y += sk) {
  127.         for (x = incr+y; x <= gMaxX; x += sk) {
  128.             d1 = GetZValue(x-incr, y);
  129.             d2 = GetZValue(x+incr, y);
  130.             SetZValue(((d1 + d2) >> 1) + (long)(RandomDouble() * (len >> 1)) - (len >> 2), x, y);
  131.         }
  132.     }
  133. }
  134.  
  135.  
  136. /*
  137.     CalcYs
  138.  
  139.     This function calculates the fractal values in the Y direction.
  140.     It is called for each level and subdivides the existing edges
  141.     of the fractal to get the next level.
  142. */
  143. void CalcYs(long len, long incr, long sk)
  144. {
  145.     long y, x;
  146.     long d1, d2;
  147.  
  148.     for (x = gMaxX; x >= 1; x -= sk)
  149.         for (y = incr; y <= x; y += sk) {
  150.             d1 = GetZValue(x, y + incr);
  151.             d2 = GetZValue(x, y - incr);
  152.             SetZValue(((d1 + d2) >> 1) + (long)(RandomDouble() * (len >> 1)) - (len >> 2), x, y);
  153.         }
  154. }
  155.  
  156.  
  157. /*
  158.     CalcDiagonals
  159.  
  160.     This function calculates the fractal values in the diagonal direction.
  161.     It is called for each level and subdivides the existing edges
  162.     of the fractal to get the next level.
  163. */
  164. void CalcDiagonals(long len, long incr, long sk)
  165. {
  166.     long             y, x;
  167.     long             d1, d2;
  168.         
  169.     for (x = 0; x < gMaxX; x += sk)
  170.     for (y = incr; y <= gMaxX - x; y += sk) {
  171.         d1 = GetZValue(x + y - incr, y - incr);
  172.         d2 = GetZValue(x + y + incr, y + incr);
  173.         SetZValue(((d1 + d2) >> 1) + (long)(RandomDouble() * (len >> 1)) - (len >> 2), x + y, y);
  174.     }
  175. }
  176.  
  177.  
  178. /*
  179.     GetZValue
  180.  
  181.     This function returns the Z value (i.e. the value stored in the
  182.     two-dimensional gPlotArray array. Because the fractal is triangular
  183.     and the array is square, we will store half the triange in the lower
  184.     part of the array the the other half in the upper part of the
  185.     array.
  186. */
  187. long GetZValue(long x, long y)
  188. {
  189.     if (y <= gMaxY)
  190.         return (*gPointArray)[x][y];
  191.     else
  192.         return (*gPointArray)[gMaxX - x][gMaxX + 1 - y];
  193. }
  194.  
  195.  
  196. /*
  197.     SetZValue
  198.  
  199.     This function saves the Z value (i.e. the value in the
  200.     two-dimensional gPlotArray array. Because the fractal is triangular
  201.     and the array is square, we will store half the triange in the lower
  202.     part of the array the the other half in the upper part of the
  203.     array.
  204. */
  205. void SetZValue(long d, long x, long y)
  206. {
  207.     if (y <= gMaxY)
  208.         (*gPointArray)[x][y] = d;
  209.     else
  210.         (*gPointArray)[gMaxX - x][gMaxX + 1 - y] = d;
  211. }
  212.  
  213.  
  214. /*
  215.     PlotData
  216.     
  217.     Our window is already open at this point, and it is cleared.
  218.     All we have to do now is fill it. This function draws the 
  219.     2D projection of the triangular database on the screen.
  220. */
  221. void PlotData(void)
  222. {
  223.     long             xindex, yindex;
  224.  
  225.     gPixelBase = (char*)GetPixBaseAddr(gOffscreenPixMap);
  226.     gRowBytesIncrement = (*gOffscreenPixMap)->rowBytes & 0x7FFF;
  227.  
  228.     gWindowScaleX = (kNewScreenX << kScaleShift) / kOriginalScreenX;
  229.     gWindowScaleY = (kNewScreenY << kScaleShift) / kOriginalScreenY;
  230.     
  231.     gOnLand = true;           /* On land to start. */
  232.  
  233.     for (xindex = 0; xindex <= gMaxX; xindex++) {        /* Plot along X axis. */
  234.         gAtLineStart = true;
  235.         for (yindex = 0; yindex <= xindex; yindex++)
  236.             DoPlot(xindex, yindex);
  237.     }
  238.  
  239.     for (yindex = 0; yindex <= gMaxX; yindex++) {        /* Plot along Y axis. */
  240.         gAtLineStart = true;
  241.         for (xindex = yindex; xindex <= gMaxX; xindex++)
  242.             DoPlot(xindex, yindex);
  243.     }
  244.  
  245.     for (xindex = 0; xindex <= gMaxX; xindex++) {        /* Plot along the diagonal. */
  246.         gAtLineStart = true;
  247.         for (yindex=0; yindex <= gMaxX - xindex; yindex++)
  248.             DoPlot(xindex + yindex, yindex);
  249.     }
  250. }
  251.  
  252.  
  253. /*
  254.     DoPlot
  255.     
  256.     This function plots a single line or point from the current X and
  257.     Y value to the new X and Y value specified.
  258. */
  259. void DoPlot(long xindex, long yindex)
  260. {
  261.     long        xcoord, ycoord, zcoord;
  262.  
  263.     zcoord = GetZValue(xindex, yindex);
  264.     ycoord = (yindex * 10000) >> gLevel;
  265.     xcoord = ((xindex * 10000) >> gLevel) - ycoord/2;
  266.     if (gContourType == kStyleWater)
  267.         SeaLevel(&xcoord, &ycoord, &zcoord);
  268.     PlotTo(xcoord, ycoord, zcoord);
  269. }
  270.  
  271.  
  272. /*
  273.     SeaLevel
  274.     
  275.     This function is used to plot lines when using the “foothills
  276.     with water” mode. It determines whether a line goes under
  277.     water at any point. If it does, it simply draws a point
  278.     to represent the water.
  279. */
  280. void SeaLevel(long *newX, long *newY, long *newZ)
  281. {
  282.     static long     oldX, oldY, oldZ;            /* The starting point for the next call */
  283.     long             waterX, waterY, waterZ;     /* Where the vector hits the waterline. */
  284.     float             scratch;
  285.  
  286.     if (gAtLineStart) {                        /* If at the beginning of the line */
  287.         if ((oldZ = *newZ) < 0) {            /*  and if we’re underwater */
  288.             SetLineColor(blueColor);
  289.             gOnLand = false;
  290.             *newZ = 0;                        /* Clip to the waterline */
  291.         }
  292.         else {
  293.             SetLineColor(blackColor);
  294.             gOnLand = true;                    /* Otherwise we’re on land from the start */
  295.         }
  296.     }
  297.     else {                                    /* Else we’re in the middle of a line and */
  298.         if (oldZ > 0 && *newZ > 0) {        /* Start & end points both above water.. */
  299.             oldZ = *newZ;
  300.         }
  301.         else if (oldZ < 0 && *newZ < 0) {    /* Start & end points both under water... */
  302.             oldZ = *newZ;
  303.             *newZ = 0;                        /* Clip at the waterline */
  304.         }
  305.         else {                                /* We’re now crossing the waterline, so calculate */
  306.                                             /*  the exact point where it dives under */
  307.             if (*newZ == oldZ) {
  308.                 waterX = oldX;
  309.                 waterY = oldY;
  310.             }
  311.             else {
  312.                 scratch = (float) *newZ / (*newZ - oldZ);            /* Proportion of the line that’s */
  313.                 waterX = (long) ((oldX - *newX) * scratch) + *newX;    /*  below the water */
  314.                 waterY = (long) ((oldY - *newY) * scratch) + *newY;
  315.             }
  316.             waterZ = 0;
  317.  
  318.             PlotTo(waterX, waterY, waterZ);    /* Draw to the waterline first */
  319.             
  320.             /* The plot from the waterline to the endpoint in the new color is done elsewhere */
  321.             if (*newZ > 0) {                /* Emerging from the water */
  322.                 SetLineColor(blackColor);
  323.                 gOnLand = true;
  324.                 oldZ = *newZ;
  325.             }
  326.             else {                            /* Diving into the water. */
  327.                 SetLineColor(blueColor);
  328.                 gOnLand = false;
  329.                 oldZ = *newZ;
  330.                 *newZ = 0;
  331.             }
  332.         }
  333.     }
  334.     oldX = *newX;                            /* Save the real endpoint of the vector */
  335.     oldY = *newY;                            /*  to use as the start of the next call */
  336. }
  337.  
  338.  
  339. /*
  340.     PlotTo
  341.     
  342.     This function converts a 3-D line to a 2-D line and plots it
  343. */
  344. void PlotTo(long x, long y, long z)
  345. {
  346.     long    tempNum;
  347.  
  348.     Rotate(&x, &y);                         /* Rotate 30 deg. towards Y in the XY plane */
  349.     TiltDown(&x, &z);                       /* Tip 36 deg. down in the ZX plane */
  350.     
  351.     tempNum = y;                            /* Compiler generates better code with temp register */
  352.     y = (tempNum >> 5) + (tempNum >> 7);    /* Scale 10K to 400. */
  353.     tempNum = z;
  354.     z = (tempNum >> 5) + (tempNum >> 7);
  355.     
  356.     DrawLine(y, z);                            /* Show the YZ planar projection. */
  357. }
  358.  
  359.  
  360. /*
  361.     DrawLine
  362.     
  363.     This function draws a line from the last endpoint
  364. */
  365. void DrawLine(long x, long y)
  366. {
  367.     x += (x >> 4) + (x >> 5);
  368.     x += 10;                                 /* Compute x 1.09 + 10 */
  369.     if (gContourType == kStyleMountains)
  370.             y = 220 - y;                    /* Move the baseline for mountains */
  371.     else
  372.             y = 80 - y;
  373.  
  374.     if (!gOnLand) {
  375.         ScaledPoint(x, y);
  376.     }
  377.     else {
  378.         if (gAtLineStart) {
  379.             ScaledMoveTo(x, y);
  380.             gAtLineStart = false;
  381.         }
  382.         ScaledLineTo(x, y);
  383.     }
  384. }
  385.  
  386.  
  387. /*
  388.     Cordic
  389.     
  390.     This function spins the XY vector ‘count’ steps to the left 
  391.     using a CORDIC algorithm with a shift factor of ‘scale.’
  392.     Rotates atan(1/(2^scale)) degrees/step (e.g. scale of 5 is 
  393.     1.79 deg/step;  4 = 3.57 d/s...)
  394.     
  395.     *x and *y should be large for accuracy.
  396. */
  397. void Cordic(long *x, long *y, long scale, long count)      
  398. {
  399.     long            tempX, tempY;
  400.  
  401.     tempX = *x;
  402.     tempY = *y;
  403.     if (count > 0)                        /* Positive count is CCW (left) */
  404.         for (; count; count--) {
  405.             tempX -= (tempY >> scale);
  406.             tempY += (tempX >> scale);
  407.         }
  408.     else                                /* Negative is CW (right) */
  409.         for (; count; count++) {
  410.             tempX += (tempY >> scale);
  411.             tempY -= (tempX >> scale);
  412.         }
  413.     *x = tempX;
  414.     *y = tempY;
  415. }
  416.  
  417.  
  418. /*
  419.     ScaledMoveTo
  420.     
  421.     This function performs a scaled MoveTo by scaling the given
  422.     point from MacPlus screen coordinates to the size of the
  423.     current main window.
  424. */
  425. void ScaledMoveTo(long x, long y)
  426. {
  427.     gCurrentX = (x * gWindowScaleX) >> kScaleShift;
  428.     gCurrentY = (y * gWindowScaleY) >> kScaleShift;
  429. }
  430.  
  431.  
  432. /*
  433.     ScaledLineTo
  434.  
  435.     This function performs a scaled LineTo by scaling the given
  436.     point from MacPlus screen coordinates to the size of the
  437.     current main window.
  438. */
  439. void ScaledLineTo(long x, long y)
  440. {
  441.     LineToOffscreen((x * gWindowScaleX) >> kScaleShift, 
  442.             (y * gWindowScaleY) >> kScaleShift);
  443. }
  444.  
  445.  
  446. /*
  447.     ScaledPoint
  448.  
  449.     This function draws a scaled pixel by scaling the given
  450.     point from MacPlus screen coordinates to the size of the
  451.     current main window.
  452. */
  453. void ScaledPoint(long x, long y)
  454. {
  455.     PointOffscreen((x * gWindowScaleX) >> kScaleShift, 
  456.             (y * gWindowScaleY) >> kScaleShift);
  457. }
  458.  
  459.  
  460. /*
  461.     SetLineColor
  462.     
  463.     This function sets the value of the global gPixelColor 
  464.     to the appropriate value for the give color.
  465. */
  466. void SetLineColor(unsigned short colorCode)
  467. {
  468.     RGBColor        curColor;
  469.  
  470.     ForeColor(colorCode);
  471.     GetForeColor(&curColor);
  472.  
  473.     gPixelColor = Color2Index(&curColor);
  474. }
  475.  
  476.  
  477. /*
  478.     LineToOffscreen
  479.     
  480.     This function replaces the LineTo function with a custom
  481.     off-screen line drawing function.
  482. */
  483. void LineToOffscreen(long x, long y)
  484. {
  485.     if (AbsoluteValue(y - gCurrentY) > AbsoluteValue(x - gCurrentX)) {
  486.         if (y < gCurrentY)
  487.             LineYOffscreen(x, y, gCurrentX, gCurrentY);
  488.         else
  489.             LineYOffscreen(gCurrentX, gCurrentY, x, y);
  490.     }
  491.     else {
  492.         if (x < gCurrentX)
  493.             LineXOffscreen(x, y, gCurrentX, gCurrentY);
  494.         else
  495.             LineXOffscreen(gCurrentX, gCurrentY, x, y);
  496.     }
  497.     
  498.     gCurrentX = x;
  499.     gCurrentY = y;
  500. }
  501.  
  502.  
  503. /*
  504.     LineXOffscreen
  505.  
  506.     Draws lines with a slope between -1 and 1
  507. */
  508. void LineXOffscreen(long startX, long startY, long endX, long endY)
  509. {
  510.     long            currentX;
  511.     float            slope, currentY;
  512.     char*            pixelAddress;
  513.     long            deltaY;
  514.     float            newY;
  515.     
  516.     if (endX == startX)
  517.         slope = 0;
  518.     else
  519.         slope = (float)(endY - startY) / (float)(endX - startX);
  520.  
  521.     pixelAddress = gPixelBase + startX;
  522.     pixelAddress += (unsigned long)(startY * gRowBytesIncrement);
  523.     currentY = startY + (float)0.5;
  524.     
  525.     for (currentX = startX; currentX <= endX; currentX++) {
  526.         *pixelAddress++ = gPixelColor;
  527.         newY = currentY + slope;
  528.         deltaY = (long)newY - (long)currentY;
  529.         if (deltaY)
  530.             pixelAddress += (long)(gRowBytesIncrement * deltaY);
  531.         currentY = newY;
  532.     }
  533. }
  534.  
  535.  
  536. /*
  537.     LineYOffscreen
  538.  
  539.     Draws lines with a slope <= -1 or >= 1
  540. */
  541. void LineYOffscreen(long startX, long startY, long endX, long endY)
  542. {
  543.     long            currentY;
  544.     float            slope, currentX, newX;
  545.     char*            pixelAddress;
  546.     
  547.     if (endY == startY)
  548.         slope = 0;
  549.     else
  550.         slope = (float)(endX - startX) / (float)(endY - startY);
  551.  
  552.     pixelAddress = gPixelBase + startX;
  553.     pixelAddress += (unsigned long)(startY * gRowBytesIncrement);
  554.     currentX = startX + 0.5;
  555.     
  556.     for (currentY = startY; currentY <= endY; currentY++) {
  557.         *pixelAddress = gPixelColor;
  558.         newX = currentX + slope;
  559.         pixelAddress += gRowBytesIncrement + (long)(newX) - (long)(currentX);
  560.         currentX = newX;
  561.     }
  562. }
  563.  
  564.  
  565. /*
  566.     PointOffscreen
  567.  
  568.     Draws a single pixel
  569. */
  570. void PointOffscreen(long x, long y)
  571. {
  572.     char*            pixelAddress;
  573.  
  574.     pixelAddress = gPixelBase + x;
  575.     pixelAddress += (unsigned long)(y * gRowBytesIncrement);
  576.     *pixelAddress = gPixelColor;
  577.  
  578.     gCurrentX = x;
  579.     gCurrentY = y;
  580. }
  581.  
  582.  
  583.  
  584.  
  585.  
  586.  
  587.  
  588.